from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.common.exceptions import TimeoutException, ElementClickInterceptedException, ElementNotInteractableException
from selenium.webdriver.remote.webelement import WebElement
import time
import sys

def TemplateFunction(
    driver: webdriver.WebDriver,
    target: str,
    step_name: str = "步骤",
    preconditions: str = "//",
    postcondition: str = "//",
    timeout: int = 10,
    terminate_on_failure: bool = False
) -> WebElement:
    """执行带前置条件和后置条件验证的元素操作。

    该函数用于自动化测试中执行元素操作（如点击），并在操作前后验证页面状态，
    确保测试环境符合预期。支持在条件失败时选择终止程序或继续执行。

    Args:
        driver (WebDriver): Selenium WebDriver实例，用于控制浏览器。
        target (str): 目标元素的XPath定位表达式。
        step_name (str, optional): 步骤名称，用于日志提示。默认为"步骤"。
        preconditions (str, optional): 前置条件元素的XPath定位表达式。
            设置为"//"表示跳过前置条件验证。默认为"//"。
        postcondition (str, optional): 后置条件元素的XPath定位表达式。
            设置为"//"表示跳过后置条件验证。默认为"//"。
        timeout (int, optional): 元素等待超时时间（秒）。默认为10。
        terminate_on_failure (bool, optional): 条件验证失败时是否终止程序。
            True: 立即终止程序（通过sys.exit(1)）。
            False: 返回None并继续执行后续步骤。默认为False。

    Returns:
        WebElement: 操作成功时返回目标元素对象，失败时返回None（仅当terminate_on_failure=False）。

    Raises:
        此函数不直接抛出异常，而是通过print输出错误信息，并根据terminate_on_failure参数决定是否终止程序。

    Examples:
        # 简单点击操作，不验证条件
        TemplateFunction(driver, "//button[@id='submit']", "提交表单")

        # 带前置和后置条件验证的操作
        TemplateFunction(
            driver,
            target="//a[text()='登录']",
            step_name="点击登录",
            preconditions="//input[@id='username']",
            postcondition="//div[@class='success-message']",
            terminate_on_failure=True
        )
    """
    wait = WebDriverWait(driver, timeout)
    
    print(f"提示 : 当前正在执行 [{step_name}] 步骤...")
    
    # 前置条件验证
    if preconditions != "//":
        print(f"提示 : 正在验证 [{step_name}] 前置条件...")
        try:
            wait.until(EC.presence_of_element_located((By.XPATH, preconditions)),f"前置条件元素未找到: {preconditions}")
            print(f"提示 : [{step_name}] 前置条件验证通过")
        except TimeoutException:
            error_msg = f"错误 : [{step_name}] 前置条件未满足，无法执行操作"
            print(error_msg)
            if terminate_on_failure:
                sys.exit(1)
            return None
    
    # 查找并操作目标元素
    element = None
    try:
        # 查找元素
        element = wait.until(EC.presence_of_element_located((By.XPATH, target)),f"目标元素未找到: {target}")
        
        # 滚动到元素位置
        driver.execute_script("arguments[0].scrollIntoView({block: 'center', inline: 'center'});",element)
        time.sleep(0.5)  # 等待滚动完成
        
        # 执行点击操作
        try:
            element.click()
            print(f"提示 : 成功点击目标元素: {target}")
        except (ElementClickInterceptedException, ElementNotInteractableException):
            # 尝试JS点击作为备选方案
            driver.execute_script("arguments[0].click();", element)
            print(f"提示 : 通过JS点击目标元素: {target}")
            
    except TimeoutException as te:
        error_msg = f"错误 : [{step_name}] 查找元素超时: {str(te)}"
        print(error_msg)
        if terminate_on_failure:
            sys.exit(1)
        return None
    except Exception as e:
        error_msg = f"错误 : [{step_name}] 执行过程中发生异常: {str(e)}"
        print(error_msg)
        if terminate_on_failure:
            sys.exit(1)
        return None
    
    # 后置条件验证（与目标操作平级，不再嵌套）
    if postcondition != "//" and element:
        print(f"提示 : 正在验证 [{step_name}] 后置条件...")
        try:
            wait.until(EC.presence_of_element_located((By.XPATH, postcondition)),f"后置条件元素未找到: {postcondition}")
            print(f"提示 : [{step_name}] 后置条件验证通过，操作成功")
        except TimeoutException:
            error_msg = f"警告 : [{step_name}] 后置条件未满足，操作可能失败"
            print(error_msg)
            if terminate_on_failure:
                sys.exit(1)
    
        finally:
            print(f"提示 : [{step_name}] 步骤执行完毕!\n")
            return element
        



# 参数1 WebDriver 实例对象
# 参数2 target 元素的定位表达式
# 参数3 step_name 步骤名称
# 参数4 preconditions 前置条件元素定位表达式
# 参数5 preconditions 前置条件元素对应的条件类型
# 参数6 postconditions 后置条件元素定位表达式
# 参数7 postconditions 后置条件元素对应的条件类型
# 参数8 timeout 等待超时时间（秒）
# 参数9 retry_count 重试次数
# 参数10 terminate_on_failure 是否在失败时终止执行

# 关于这个元素对应的条件类型.
# 你可以单独设计一个简单的枚举类型,暂时就添加两个内容,一个条件是宽度大于50,一个是宽度小于50.